/*
 * cudanDeviceMemory.cpp
 *
 *  Created on: Dec 8, 2009
 *      Author: daniel
 */

namespace CUDAN
{
	// Default Constructor.
	template<typename T, int D>
	DeviceMemory<T,D>::DeviceMemory() : BaseMemory<T,D>()
	{
#ifdef _DEBUG
		std::cout << "DeviceMemory<T,D>::DeviceMemory()" << std::endl;
#endif
	}

	// Returns a linear index using an N dimensional index, index, with the members dimension size.
	template<typename T, int D>
	int DeviceMemory<T,D>::getIndex(int index[D])
	{
		// Warning. No boundary checking.

		// Calculate the linear index.
		int l = index[D-1];

		for (int i = (D-2); i >= 0; --i  )
		{
			l *= this->sizeDimGPU[i];
			l += index[i];
		}

		return l;
	}

	// Returns a linear index using an N dimensional index, index, with a dimension size, dim.
	// Different to getIndex(DimensionSizeVector& index) in that the size of the dimensions
	// is specified by the user, not the members dimension size.
	template<typename T, int D>
	int DeviceMemory<T,D>::getIndex(int index[D], int dim[D])
	{
		// Warning. No boundary checking.

		// Calculate the linear index.
		int l = index[D-1];

		for (int i = (D-2); i >= 0; --i  )
		{
			l *= dim[i];
			l += index[i];
		}

		return l;
	}

	// Sets the number of elements and dimension size of the memory.
	// Copies the dimension size to the device in global.
	template<typename T, int D>
	bool DeviceMemory<T,D>::setSize(DimensionSizeVector& dim)
	{
#ifdef _DEBUG
		std::cout << "bool DeviceMemory<T,D>::setSize(DimensionSizeVector& dim)" << std::endl;
#endif
		// Boundary check dimensions.
		for(unsigned int i = 0; i < D; i++) if(dim[i] < 1) return false;

		// Set the dimension size.
		this->sizeDim = dim;

		// Set the number of elements in memory.
		this->size = this->getSize(dim);

		// Allocate memory on the device for the dimension size.
		cudaMalloc((void**)&this->sizeDimGPU, sizeof(int) * D);

		// Copy across the dimension size to the device.
		for(unsigned int i=0; i<D; i++) cudaMemcpy(&this->sizeDimGPU[i], &this->sizeDim[i], sizeof(int), cudaMemcpyHostToDevice);

		return true;
	}
}
